/*
 * Copyright (c) 2009-2011, okazoh_tk. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  - Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef ALKES_IGRAPHICS_2D_DEVICE_H_INCLUDED_
#define ALKES_IGRAPHICS_2D_DEVICE_H_INCLUDED_

#include "alkes/gfx/IGraphicsDevice.h"
#include "alkes/gfx/Shape.h"
#include "alkes/gfx/LineShape.h"
#include "alkes/gfx/RectangleShape.h"
#include "alkes/gfx/ImageShape.h"
#include "alkes/gfx/PolylineShape.h"

namespace alkes {

class RenderTarget;

/// \addtogroup graphics
//@{

enum TargetBehavior
{
    AL_BEHAVIOR_DISCARD = 0x00,                 ///< Discard
    AL_BEHAVIOR_SINGLE  = 0x01,                 ///< Single buffer
    AL_BEHAVIOR_FLIP    = 0x10,
    AL_BEHAVIOR_DOUBLE = AL_BEHAVIOR_FLIP + 1,  ///< Double buffering
    AL_BEHAVIOR_TRIPLE,                         ///< Triple buffering
};

enum AlphaBlendMode
{
    AL_BLEND_ZERO = 0,      ///< (0,0,0,0)
    AL_BLEND_ONE,           ///< (1,1,1,1)
    AL_BLEND_SRCCOLOR,      ///< (Rs,Gs,Bs,As)
    AL_BLEND_INVSRCCOLOR,   ///< (1-Rs,1-Gs,1-Bs,1-As)
    AL_BLEND_DSTCOLOR,      ///< (Rd,Gd,Bd,Ad)
    AL_BLEND_INVDSTCOLOR,   ///< (1-Rd,1-Gd,1-Bd,1-Ad)
    AL_BLEND_SRCALPHA,      ///< (As,As,As,As)
    AL_BLEND_INVSRCALPHA,   ///< (1-As,1-As,1-As,1-As)
    AL_BLEND_DSTALPHA,      ///< (Ad,Ad,Ad,Ad)
    AL_BLEND_INVDSTALPHA,   ///< (1-Ad,1-Ad,1-Ad,1-Ad)
};

enum TexFilterValue
{
    AL_TEXFILTER_VALUE_POINT = 0,
    AL_TEXFILTER_VALUE_BILINEAR,
};

enum TexAddressMode
{
    AL_TEXADDRESS_MODE_DISABLE = 0,
    AL_TEXADDRESS_MODE_WRAP,
    AL_TEXADDRESS_MODE_CLAMP,
};

enum Graphics2DState
{
    AL_GS_ALPHA_BLEND = 0,  // alpha blend
    AL_GS_LINE_SMOOTH,      // Line antialias
    AL_GS_POLYGON_SMOOTH,   // Polygon antialias

    AL_GS_3D_STATE_BEGIN = 200, // for system
};

#define AL_OPACITY_MAX  (100)


class AL_EXPORT IGraphics2DDevice
    : public IGraphicsDevice
{
    AL_DECLARE_CLASS(IGraphics2DDevice, IGraphicsDevice);

public:
    bool correctTexelMapping() const;

    virtual RenderTarget* createDefaultRenderTarget() = 0;

    /** Take snapshot image.
    @param [in] writer Writer object.
    @param [in] rect 
    */
    void snapshot(
        ImageWriter& writer,
        const Rect* rect = 0);

    virtual Shape* createShape(int32_t shape_type, uint32_t num);
    virtual LineShape* createLineShape(uint32_t num);
    virtual RectangleShape* createRectangleShape(uint32_t num);
    virtual ImageShape* createImageShape(uint32_t num);
    virtual PolylineShape* createPolylineShape(uint32_t num);

    ImageShape* createImageShape(uint32_t num, ImageBuffer* image, const Point2D* dst_xys);
    ImageShape* createImageShape(ImageBuffer* image, const Point2D& dst_xy);

    ImageShape* createImageShape(uint32_t num, ImageBuffer* grayscale_image, const Point2D* dst_xys, const Color& color);
    ImageShape* createImageShape(ImageBuffer* grayscale_image, const Point2D& dst_xy, const Color& color);

    /** Begin draw 2D.
    */
    virtual void beginDraw2D(RenderTarget* target) = 0;

    /** End draw 2D.
    */
    virtual void endDraw2D() = 0;

    /** Get rectangle of this target.
    @return Rectangle of this rendering target.
    */
    virtual const Rect getTargetRect() const = 0;

    /** Get current RenderTarget object.
     * @return 
     */
    virtual RenderTarget* getRenderTarget() = 0;

    /** Set scissor rectangle.
    @param [in] rect
    */
    virtual void setScissorRect(
        const Rect& rect) = 0;

    /** Get current scissor rectangle.
    @return current scissor rectangle.
    */
    virtual const Rect getScissorRect() const = 0;

    /** Set rendering state.
    @param [in] state
    @param [in] enable
    */
    virtual void setState(int state, bool enable) = 0;

    /** Get rendering state.
    @param [in] state
    @retval true State is enable.
    @retval false State is disable.
    @sa Graphics2DState, Graphics3DState
    */
    virtual bool getState(int state) = 0;

    /** Set alpha blend mode.
    @param [in] src_blend New AlphaBlendMode of source.
    @param [in] dst_blend New AlphaBlendMode of destination.
    @sa AlphaBlendMode
    */
    virtual void setAlphaBlendMode(
        AlphaBlendMode src_blend,
        AlphaBlendMode dst_blend) = 0;

    /** Get alpha blend mode.

    @param [out] src_blend AlphaBlendMode of source.
    @param [out] dst_blend AlphaBlendMode of destination.
    @sa AlphaBlendMode
    */
    virtual void getAlphaBlendMode(
        AlphaBlendMode* src_blend,
        AlphaBlendMode* dst_blend) const = 0;

    /** Set texture filter.
    @param [in] min_filter New TexFilterValue of min.
    @param [in] mag_filter New TexFilterValue of mag.
    @sa TexFilterValue
    */
    virtual void setTextureFilter(
        TexFilterValue min_filter,
        TexFilterValue mag_filter) = 0;

    /** Get texture filter.
    @param [out] min_filter TexFilterValue of min.
    @param [out] mag_filter TexFilterValue of mag.
    @sa TexFilterValue
    */
    virtual void getTextureFilter(
        TexFilterValue* min_filter,
        TexFilterValue* mag_filter) = 0;

    /** Set texture addressing.
    @param [in] u_mode New TexAddressMode of u coord.
    @param [in] v_mode New TexAddressMode of v coord.
    @sa TexAddressMode.
    */
    virtual void setTextureAddressMode(
        TexAddressMode u_mode,
        TexAddressMode v_mode) = 0;

    /** Get texture addressing.
    @param [out] u_mode New TexAddressMode of u coord.
    @param [out] v_mode New TexAddressMode of v coord.
    @sa TexAddressMode.
    */
    virtual void getTextureAddressMode(
        TexAddressMode* u_mode,
        TexAddressMode* v_mode) = 0;

    // rendering

    /** Fill this render target.
    @param [in] color
    */
    virtual void fill(const Color& color) = 0;

    /** Fill all render target.
    @param [in] color
    @retval true
    @retval false
    @post Reset scissor rect to full size.
    */
    void fillAll(const Color& color);

    virtual bool drawShape(Shape* shape, uint32_t start = 0, uint32_t count = 1);
    virtual bool drawShape(LineShape* line_shape, uint32_t start = 0, uint32_t count = 1) = 0;
    virtual bool drawShape(RectangleShape* rectangle_shape, uint32_t start = 0, uint32_t count = 1) = 0;
    virtual bool drawShape(ImageShape* image_shape, uint32_t start = 0, uint32_t count = 1) = 0;
    virtual bool drawShape(PolylineShape* polyline_shape, uint32_t start = 0, uint32_t count = 1) = 0;

    // transforms
    virtual void pushMatrix() = 0;
    virtual void popMatrix() = 0;
    virtual void translate(const Vector2D& offset);
    virtual void translate(float x, float y) = 0;
    virtual void translateForImage(float x, float y);
    virtual void scale(float xscale, float yscale, const Vector2D& origin = Vector2D::ZERO) = 0;
    virtual void scale(float scaling, const Vector2D& origin = Vector2D::ZERO);
    virtual void rotate(float angle, const Vector2D& origin = Vector2D::ZERO) = 0;
    virtual void skewX(float angle) = 0;
    virtual void skewY(float angle) = 0;

protected:
    enum Attribute
    {
        CORRECT_TEXEL_MAPPING = 0,
    };

    IGraphics2DDevice();
    virtual ~IGraphics2DDevice() = 0;

    void setAttribute(int attr, bool value);

    virtual void onSnapshot(ImageWriter& writer, const Rect& rect) = 0;

};

//@}

}

#endif
